﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;

namespace WikiKeeper
{
	public class Program
	{
		public static void Main(string[] args)
		{
			string dir = DetermineDirectory(args.FirstOrDefault());

			if (!string.IsNullOrWhiteSpace(dir) && Directory.Exists(dir))
			{
				Run(dir);
			}
			else
			{
				Console.WriteLine("Could not automatically determine wiki directory, or specified directory does not exist.");
				Console.WriteLine("Please specify a valid directory as an argument.");
			}

			Console.WriteLine("Done.");
			Console.ReadKey(false);
		}

		public static void Run(string dir)
		{
			const string match = "*.wiki";
			IEnumerable<string> files = Directory.EnumerateFiles(dir, match, SearchOption.TopDirectoryOnly);

			if (files.Count() < 1)
			{
				Console.WriteLine("Could not find any files in {0} matching {1}.", dir, match);
				return;
			}

			Console.WriteLine("Updating line breaks...");
			foreach (string f in files)
				FixLineBreaks(f);

			Console.WriteLine("Updating includes...");
			foreach (string f in files)
				AddIncludes(dir, f);

			Console.WriteLine("Updating sidebar directives...");
			foreach (string f in files)
				UpdateSidebar(f);
		}

		public static string DetermineDirectory(string manualDir)
		{
			const string trunk = @"\trunk\";
			const string wiki = @"\wiki\";
			string dir;

			if (!string.IsNullOrWhiteSpace(manualDir))
			{
				return manualDir;
			}

			dir = Directory.GetCurrentDirectory().TrimEnd('\\') + '\\';
			int i = dir.LastIndexOf(trunk);

			if (i < 0)
			{
				i = dir.LastIndexOf(wiki);
			}

			if (i < 0)
			{
				return null;
			}

			return dir.Remove(i) + wiki;
		}

		public static void UpdateSidebar(string file)
		{
			const string directive = "#sidebar WikiIndex";
			List<string> lines = new List<string>(File.ReadAllLines(file));

			if (!lines.Remove(directive))
			{
				return;
			}

			File.WriteAllText(file, string.Join("\n", lines));
			Console.WriteLine("Removed global sidebar directive from {0}.", file.Split('\\').Last());
		}

		public static void FixLineBreaks(string file)
		{
			string text = File.ReadAllText(file);

			if (!text.Contains('\r'))
			{
				return;
			}

			File.WriteAllText(file, text.Replace("\r\n", "\n").Replace("\r", "\n"));
			Console.WriteLine("Fixed line breaks in {0}.", file.Split('\\').Last());
		}

		public static void AddIncludes(string dir, string file)
		{
			string text = File.ReadAllText(file);
			string[] meta = new string[] { "#summary", "#labels", "#sidebar" };
			MatchCollection matches = Regex.Matches(text, @"(<wiki:comment>)?\[\[(?<inc>[a-z0-9_\-]+)\]\](</wiki:comment>)?(.*?<wiki:comment>\[END OF INCLUDE\]</wiki:comment>)?", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture);

			if (matches.Count < 1)
			{
				return;
			}

			foreach (Match m in from Match m in matches
								orderby m.Index descending
								select m)
			{
				const string format = "<wiki:comment>[[{0}]]</wiki:comment>\n" +
					"<wiki:comment>DO NOT MODIFY! Any edits made here will be OVERWRITTEN with the contents of {0}.wiki.</wiki:comment>\n" +
					"{1}\n" +
					"<wiki:comment>[END OF INCLUDE]</wiki:comment>\n";

				string doc;
				string inc = m.Groups["inc"].Value;
				string path = dir + inc + ".wiki";

				if (!inc.All((c) => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-'))
				{
					doc = "_Include directive specified an invalid filename._";
				}
				else if (!File.Exists(path))
				{
					doc = "_Include directive specified a non-existent file._";
				}
				else
				{
					List<string> lines = new List<string>(File.ReadAllLines(path));

					for (string l = lines.FirstOrDefault(); lines.Count > 0; lines.RemoveAt(0), l = lines.FirstOrDefault())
					{
						if (string.IsNullOrWhiteSpace(l))
						{
							continue;
						}

						l = l.Split(' ')[0].ToLower();

						if (!meta.Contains(l))
						{
							break;
						}
					}

					doc = string.Join("\n", lines);
				}

				int suffix = m.Length + m.Index + 1;
				text = text.Remove(m.Index) + string.Format(format, inc, doc) + (suffix < text.Length ? text.Substring(m.Index + m.Length + 1) : "");
				Console.WriteLine("Included {0}.wiki in {1}.", inc, file.Split('\\').Last());
			}

			File.WriteAllText(file, text);
		}
	}
}
